package cn.ava.ymcc.service.impl;

import cn.ava.ymcc.constants.CommonConstants;
import cn.ava.ymcc.doc.CourseDoc;
import cn.ava.ymcc.domain.*;
import cn.ava.ymcc.dto.CourseSaveDto;
import cn.ava.ymcc.dto.CourseTypeCrumbsDto;
import cn.ava.ymcc.dto.LoginTokenInfoDto;
import cn.ava.ymcc.feign.MediaFeignClient;
import cn.ava.ymcc.feign.SearchFeignClient;
import cn.ava.ymcc.mapper.CourseMapper;
import cn.ava.ymcc.mq.listener.OnLineCourseProducer;
import cn.ava.ymcc.query.CourseQuery;
import cn.ava.ymcc.result.JSONResult;
import cn.ava.ymcc.service.*;
import cn.ava.ymcc.utils.AssertUtil;
import cn.ava.ymcc.utils.BaiDuAiCensorUtil;
import cn.ava.ymcc.utils.LoginInfoContextUtil;
import cn.ava.ymcc.vo.Course2OrderVo;
import cn.ava.ymcc.vo.CourseInfoVo;
import cn.ava.ymcc.vo.Courseinfo2OrderinfoVo;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ava
 * @since 2022-08-09
 */
@Service
@Transactional(readOnly = true,propagation = Propagation.NOT_SUPPORTED)
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements ICourseService {

    @Autowired
    private CourseMapper courseMapper;
    @Autowired
    private ICourseDetailService courseDetailService;
    @Autowired
    private ICourseMarketService courseMarketService;
    @Autowired
    private ICourseTypeService courseTypeService;
    @Autowired
    private ICourseResourceService courseResourceService;
    @Autowired
    private ICourseTeacherService courseTeacherService;
    @Autowired
    private ICourseSummaryService courseSummaryService;
    @Autowired
    private ICourseChapterService courseChapterService;
    @Autowired
    private ITeacherService teacherService;
    @Autowired
    private SearchFeignClient searchFeignClient;
    @Autowired
    private OnLineCourseProducer courseProducer;
    @Autowired
    private MediaFeignClient mediaFeignClient;
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;


    /**
     * 课程上线,上线成功之后,需要同步将课程信息添加进入es中
     * @param courseId
     */
    @Override
    @Transactional
    public void onLineCourse(Long courseId) {
        // 参数非空校验
        AssertUtil.isNotNull(courseId, "参数异常,请重试!");
        // 查询出课程信息进行校验
        Course course = courseMapper.selectById(courseId);
        AssertUtil.isFalse(course == null, "课程不存在,请重试!");
        AssertUtil.isFalse(course.getStatus() == 1, "课程已经上线啦!");
        // 使用百度ai校验课程信息是否合规
        AssertUtil.isTrue(BaiDuAiCensorUtil.TextCensor(course.getName()), "课程信息不合规,请修改后上线!");
        // 修改课程状态为上线,上架时间
        course.setOnlineTime(new Date());
        course.setStatus(1);
        courseMapper.updateById(course);
        // 把课程信息添加进es中
        JSONResult jsonResult = insertCourseEs(course);
        AssertUtil.isTrue(jsonResult.isSuccess(), jsonResult.getMessage());
        // 发送新课程上线消息给所有用户
        String courseName = course.getName();
        // 发送站内消息
        sendStationMsg(courseName);
        // 发送短信消息
        sendSmsMsg(courseName);
        // 发送邮件消息
        sendEmailMsg(courseName);
    }

    /**
     * 课程上线之后调用es服务把课程信息同步保存es中
     * @param course
     * @return
     */
    JSONResult insertCourseEs(Course course) {
        CourseDoc courseDoc = new CourseDoc();
        CourseMarket courseMarket = courseMarketService.selectById(course.getId());
        CourseSummary courseSummary = courseSummaryService.selectById(course.getId());
        CourseType courseType = courseTypeService.selectById(course.getCourseTypeId());
        // 直接拷贝进Doc中
        BeanUtils.copyProperties(course, courseDoc);
        BeanUtils.copyProperties(courseMarket, courseDoc);
        BeanUtils.copyProperties(courseSummary, courseDoc);
        // 判断是否是收费,存入字符串
        courseDoc.setChargeName(courseMarket.getCharge() == 1 ? "免费" : "收费");
        courseDoc.setCourseTypeName(courseType.getName());
        courseDoc.setId(course.getId());
        // 调用feign接口服务添加es数据
        JSONResult jsonResult = searchFeignClient.saveCourse(courseDoc);
        return jsonResult;
    }

    /**
     * 发送站内消息,通过mq发送消息对象到消费者处理
     * @param courseName
     */
    void sendStationMsg(String courseName) {
        MessageStation messageStation = new MessageStation();
        messageStation.setTitle("发布新课程啦");
        messageStation.setContent("全新课程:  " + courseName);
        messageStation.setType("系统消息");
        messageStation.setSendTime(new Date());
        messageStation.setIsread(0);
        messageStation.setUserId(2353L); // TODO 业务不清楚,先写死
        courseProducer.sendStationMessage(messageStation); // 发送单向消息
    }

    /**
     * 发送手机短信课程上线消息
     * @param courseName
     */
    void sendSmsMsg(String courseName) {


    }

    /**
     * 发送新课程上线邮件消息
     * @param courseName
     */
    void sendEmailMsg(String courseName) {

    }

    /**
     * @Title: offLineCourse
     * @Description: 课程下线业务流程
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/8/17 9:05
     * @param:
     * @return:
     */
    @Override
    @Transactional
    public void offLineCourse(Long courseId) {
        // 参数非空校验
        AssertUtil.isNotNull(courseId, "参数异常,请重试!");
        // 查询出课程信息进行校验
        Course course = courseMapper.selectById(courseId);
        AssertUtil.isFalse(course == null, "课程不存在,请重试!");
        AssertUtil.isFalse(course.getStatus() == 0, "课程已经下线啦!");
        // 修改课程状态为下线
        course.setStatus(0);
        courseMapper.updateById(course);
        // 删除es中的数据
        CourseDoc courseDoc = new CourseDoc();
        courseDoc.setId(courseId);
        JSONResult jsonResult = searchFeignClient.deleteCourse(courseDoc);
        AssertUtil.isTrue(jsonResult.isSuccess(), "课程下线失败,请重试!");
    }

    /**
     * @Title: crumbs
     * @Description: 前台课程列表展示面包屑功能
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/8/17 9:05
     * @param:
     * @return:
     */
    @Override
    public List<CourseTypeCrumbsDto> crumbs(Long courseTypeId) {
        // 根据id查询出courseType,获取到path
        CourseType courseType = courseTypeService.selectById(courseTypeId);
        // 校验数据是否存在
        if (courseType == null) {
            return null;
        }
        // 分割path,得到所有的类型id
        String[] ids = courseType.getPath().split("\\.");
        // 遍历每个id,获取到所有数据
        List<CourseType> courseTypes = courseTypeService.selectBatchIds(Arrays.asList(ids));
        // 放入到面包屑对象中,转换为list集合
        List<CourseTypeCrumbsDto> courseTypeCrumbsDtos = courseTypes
                .stream().map(CourseTypeCrumbsDto::new)
                .collect(Collectors.toList());
        // 遍历出所有数据的pid,根据pid获取到同级的类型数据
        for (CourseTypeCrumbsDto dto : courseTypeCrumbsDtos) {
            CourseType ownerProductType = dto.getOwnerProductType();
            // 获取到pid
            Long pid = ownerProductType.getPid();
            Wrapper<CourseType> query = new EntityWrapper<>();
            // 根据pid查询数据
            query.eq("pid", pid);
            // 通过pid找出和自己同级的类型数据
            List<CourseType> selectList = courseTypeService.selectList(query);
            // 放入到面包屑对象中
            dto.setOtherProductTypes(selectList);
        }
            return courseTypeCrumbsDtos;
    }


    /**
     * 课程详情页数据渲染
     * @param courseId
     * @return
     */
    @Override
    public CourseInfoVo CourseDetailData(Long courseId) {
        // jsr303校验参数
        Course course = selectById(courseId);
        // 校验课程是否存在
        AssertUtil.isNotNull(course,"课程不存在!");
        // 校验课程是否上线
        AssertUtil.isTrue(course.getStatus() == 1,"课程信息异常!");
        // 获取到详情数据和销售相关数据
        CourseMarket courseMarket = courseMarketService.selectById(courseId);
        CourseDetail courseDetail = courseDetailService.selectById(courseId);
        CourseSummary courseSummary = courseSummaryService.selectById(courseId);
        // 通过中间表获取到所有老师名字
        Wrapper<CourseTeacher> queryTeacher = new EntityWrapper<>();
        queryTeacher.eq("course_id",courseId);
        // 获取到所有id,转换为list
        List<Long> teacherIds = courseTeacherService.selectList(queryTeacher).stream().map(CourseTeacher::getTeacherId).collect(Collectors.toList());
        // 再通过ids查询teacher表,获取到所有老师的信息
        List<Teacher> teachers = teacherService.selectBatchIds(teacherIds);
        // 需要查询出课程下所有的章节
        Wrapper<CourseChapter> queryChapter = new EntityWrapper<>();
        queryChapter.eq("course_id",courseId);
        List<CourseChapter> courseChapters = courseChapterService.selectList(queryChapter);
        Map<Long, CourseChapter> chapterMap = courseChapters.stream().collect(Collectors.toMap(CourseChapter::getId, c -> c));
        // 还要根据课程id调用media服务,查询出所属课程对应的所有视频
        JSONResult jsonResult = mediaFeignClient.getByCourseId(courseId);
        // 校验业务合法性
        AssertUtil.isTrue(jsonResult.isSuccess(),jsonResult.getMessage());
        AssertUtil.isNotNull(jsonResult.getData(),"课程视频不存在!");
        // 获取到数据
        Object data = jsonResult.getData();
        List<MediaFile> mediaFiles = JSONObject.parseArray((String) data, MediaFile.class);
        // 遍历课程所有视频,找到每个章节对应的视频
        mediaFiles.forEach(mediaFile -> {
            // 获取到对应的课程章节
            CourseChapter courseChapter = chapterMap.get(mediaFile.getChapterId());
            // 添加章节对应的视频
            courseChapter.getMediaFiles().add(mediaFile);
        });
        return new CourseInfoVo(course,courseDetail,courseMarket,courseSummary,courseChapters,teachers);
    }


    /**
     * 课程下单页面数据渲染
     * @param courseIds
     * @return
     */
    @Override
    public Courseinfo2OrderinfoVo info(String courseIds) {
        // Jsr303非空校验
        // 截取到所有id
        String[] ids = courseIds.split(",");
        // 批量查询到所有下单课程
        List<Course> courses = selectBatchIds(Arrays.asList(ids));
        // 查询到课程销售信息,转换为map
        Map<Long, CourseMarket> marketMap = courseMarketService.selectBatchIds(Arrays.asList(ids)).stream().collect(Collectors.toMap(CourseMarket::getId, m -> m));
        // 一级封装对象,包含了课程所有信息和总金额数
        Courseinfo2OrderinfoVo Courseinfo2OrderinfoVo = new Courseinfo2OrderinfoVo();
        // 二级封装对象,用于封装课程数据
        List<Course2OrderVo> course2OrderVo = new ArrayList<>();
        // 遍历集合,放入到二级封装对象中
        courses.forEach(course -> {
            // 业务校验,课程必须为上线状态
            AssertUtil.isTrue(course.getStatus() == 1,"课程已经下线啦!");
            // 通过key获取到map中的课程销售数据
            CourseMarket courseMarket = marketMap.get(course.getId());
            // 封装到对象中
            course2OrderVo.add(new Course2OrderVo(course,courseMarket));
            // 追加总金额数
            Courseinfo2OrderinfoVo.setTotalAmount(Courseinfo2OrderinfoVo.getTotalAmount().add(courseMarket.getPrice()));
        });
        // 返回数据
        Courseinfo2OrderinfoVo.setCourseInfos(course2OrderVo);
        return Courseinfo2OrderinfoVo;
    }

    /**
     * App端课程首页展示
     * @param query
     * @return
     */
    @Override
    public Page<Course> selectPage(CourseQuery query) {
        Page<Course> page = new Page<>(query.getPage(), query.getRows());
        Wrapper<Course> wrapper = new EntityWrapper<>();
        wrapper.like("name",query.getKeyword());
        return selectPage(page,wrapper);
    }

    /**
     *  为了提高查询效率，需要把推荐的课程放入Redis，如果后台取消推荐，或者有新的课程加入推荐，需要重置Redis
     * @param courseId
     */
    @Override
    public void recommendCourse(Long courseId) {
        Course course = selectById(courseId);
        redisTemplate.opsForHash().put("recommend",course.getId().toString(),course);
    }




    /**
     * 查询出最热门的五条数据,并缓存到redis
     * @return
     */
    @Override
    @Cacheable(cacheNames = "hotCourse")
    public List<Course> hotCourse() {
        // 获取到所有统计课程表
        List<CourseSummary> courseSummaries = courseSummaryService.selectList(null);
        // 按照浏览量进行降序
        courseSummaries.sort((o1, o2) -> o2.getViewCount() - o1.getViewCount());
        // 获取到前五个最多浏览数
        List<CourseSummary> hotCourse = courseSummaries.subList(0, 5);
        // 根据id转换集合
        List<Long> ids = hotCourse.stream().map(CourseSummary::getId).collect(Collectors.toList());
        // 查询出热门课程返回,并进行缓存
        List<Course> courses = selectBatchIds(ids);
        // 过滤出上架课程
        List<Course> hotCourses = courses.stream().filter(course -> course.getStatus() == 1).collect(Collectors.toList());
        return hotCourses;
    }

    /**
     * 定时缓存热门课程
     */
    @Scheduled(cron = "0 0 */1 * * ?") // 开启定时任务,每隔一小时就缓存一次
    void taskHotCourse(){
        this.hotCourse();
    }

    /**
     * 添加新课程及课程相关表数据信息
     * @param courseSaveDto
     */
    @Override
    @Transactional
    public void save(CourseSaveDto courseSaveDto) {
        // 获取到数据对象
        Course course = courseSaveDto.getCourse();
        CourseDetail courseDetail = courseSaveDto.getCourseDetail();
        CourseMarket courseMarket = courseSaveDto.getCourseMarket();
        CourseResource courseResource = courseSaveDto.getCourseResource();
        List<Long> teacharIds = courseSaveDto.getTeacharIds();
        // 查询是否存在相同的课程
        Wrapper<Course> query = new EntityWrapper<>();
        query.eq("name",course.getName());
        // 使用工具类断言
        List<Course> courses = courseMapper.selectList(query);
        // 断言为空
        AssertUtil.isTrue(courses.isEmpty(),"该课程已存在!");
        // 查询出教师名称
        List<Teacher> teachers = teacherService.selectBatchIds(teacharIds);
        String teacherNames = teachers.stream().map(Teacher::getName).collect(Collectors.joining(","));
        // 添加课程信息
        Course newCourse = initCourse(course, teacherNames);
        // 添加课程详情信息
        initCourseDetail(courseDetail,newCourse.getId());
        // 添加课程销售信息
        initCourseMarket(courseMarket,newCourse.getId());
        // 添加课程资源信息
        initCourseResources(courseResource,newCourse.getId());
        // 添加课程讲师中间表
        initCourseTeacher(teacharIds,newCourse.getId());
        // 初始化统计表
        initCourseSummary(newCourse.getId());
        // 查询出课程类型,然后课程数量加1
        CourseType courseType = courseTypeService.selectById(newCourse.getCourseTypeId());
        courseType.setTotalCount(courseType.getTotalCount() + 1);
        courseTypeService.updateById(courseType);

    }

    /**
     * 初始化课程讲师中间表,保存对应的讲师id和课程id
     * @param teacharIds
     * @param CourseId
     */
    @Transactional
    void initCourseTeacher(List<Long> teacharIds, Long CourseId) {
        List<CourseTeacher> courseTeachers = teacharIds.stream().map(t -> {
            CourseTeacher courseTeacher = new CourseTeacher();
            courseTeacher.setCourseId(CourseId);
            courseTeacher.setTeacherId(t);
            return courseTeacher;
        }).collect(Collectors.toList());
        courseTeacherService.insertBatch(courseTeachers);

    }

    /**
     * 添加课程表,初始化表中的数据
     * @param courseDto
     * @param teacherNames
     * @return
     */
    @Transactional
    Course initCourse(Course courseDto,String teacherNames){
        Course course = new Course();
        course.setName(courseDto.getName());
        course.setForUser(courseDto.getForUser());
        course.setCourseTypeId(courseDto.getCourseTypeId());
        course.setGradeName(courseDto.getGradeName());
        course.setGradeId(courseDto.getGradeId());
        course.setStatus(CommonConstants.CourseStateConstant.STATE_ZERO);
        // 获取用户信息
        LoginTokenInfoDto loginContext = LoginInfoContextUtil.getLoginContext();
        course.setLoginId(loginContext.getLoginId());
        course.setLoginUserName(loginContext.getUsername());
        course.setStartTime(courseDto.getStartTime());
        course.setEndTime(courseDto.getEndTime());
        course.setTeacherNames(teacherNames);
        courseMapper.insert(course);
        return course;
    }

    /**
     * 初始化课程详情表数据
     * @param courseDetailDto
     * @param CourseId
     */
    @Transactional
    void initCourseDetail(CourseDetail courseDetailDto,Long CourseId){
        CourseDetail courseDetail = new CourseDetail();
        courseDetail.setId(CourseId);
        courseDetail.setDescription(courseDetailDto.getDescription());
        courseDetail.setIntro(courseDetailDto.getIntro());
        courseDetailService.insert(courseDetail);
    }

    /**
     * 初始化课程销售表信息
     * @param courseMarketDto
     * @param CourseId
     */
    @Transactional
    void initCourseMarket(CourseMarket courseMarketDto,Long CourseId){
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setId(CourseId);
        courseMarket.setCharge(courseMarketDto.getCharge());
        courseMarket.setQq(courseMarketDto.getQq());
        // 判断是否是免费课程,如果是那么就将金额初始化为0.00
        if(courseMarketDto.getCharge() == 1){
            courseMarketDto.setPrice(new BigDecimal("0.00"));
            courseMarketDto.setPriceOld(new BigDecimal("0.00"));
        }
        courseMarket.setPrice(courseMarketDto.getPrice());
        courseMarket.setPriceOld(courseMarketDto.getPriceOld());
        courseMarket.setValidDays(courseMarketDto.getValidDays());
        courseMarketService.insert(courseMarket);
    }

    /**
     * 保存课程资源表数据
     * @param courseResourceDto
     * @param CourseId
     */
    @Transactional
    void initCourseResources(CourseResource courseResourceDto, Long CourseId){
        CourseResource courseResource = new CourseResource();
        courseResource.setResources(courseResourceDto.getResources());
        courseResource.setType(courseResourceDto.getType());
        courseResource.setCourseId(CourseId);
        courseResourceService.insert(courseResource);
    }


    /**
     * 初始化课程统计信息相关表
     * @param CourseId
     */
    @Transactional
    void initCourseSummary(Long CourseId){
        CourseSummary courseSummary = new CourseSummary();
        courseSummary.setId(CourseId);
        courseSummaryService.insert(courseSummary);
    }


}
